package com.zx._12_算法.algorithm.happy;

/**
 * @version v1.0
 * @Project: knowledge
 * @Title: RedPacket
 * @Description: 模拟微信红包生成，以分为单位
 * @author: zhangxue
 * @date: 2018年12月20日下午10:26:52
 * @company: 未知之地
 * @Copyright: Copyright (c) 2018
 */
public class RedPacket {

    /**
     * 生成红包最小值 1分钱
     */
    private static final int MIN_MONEY = 1;

    /**
     * 生成红包最大值 200人民币
     */
    private static final int MAX_MONEY = 200 * 100;

    /**
     * 最大的红包是平均值的 TIMES 倍，防止某一次分配红包较大
     */
    private static final double TIMES = 2.1F;


    public static int get(RedMoney redMoney) {
        if (!checkRule(redMoney)) {
            throw new RuntimeException("初始化金额异常");
        }

        // 红包总额。单位，分
        int money = redMoney.money;
        // 红包剩余。单位，分
        int residueMoney = redMoney.residueMoney;
        // 红包数量
        int total = redMoney.total;
        // 红包剩余数量
        int residueTotal = redMoney.residueTotal;
        // 单次最大金额
        int maxMoney = (int) (1.0 * money / total * TIMES);
        // 单次最小金额
        int minMoney = MIN_MONEY;

        if (residueTotal <= 1) {//只剩下一次
            redMoney.residueMoney = 0;
            redMoney.residueTotal = 0;
            return residueMoney;
        }

        //在 minMoney到maxMoney 生成一个随机红包
        while (true) {
            int redPacket = (int) (Math.random() * (maxMoney - minMoney) + minMoney);

            redPacket = redPacket < minMoney ? minMoney : redPacket;
            redPacket = redPacket > maxMoney ? maxMoney : redPacket;

            if (redPacket > residueMoney) {//剩余金额不够分配，继续计算随机值
                continue;
            }

            // 预算分配后的剩余金额
            int residueMoneyAfter = residueMoney - redPacket;
            // 预算分配后的剩余红包数量
            int residueTotalAfter = residueMoney - 1;
            // 剩余人均分配金额,保证剩下的金额至少每人分得minMoney
            int avg = (int) (residueMoneyAfter / residueTotalAfter);
            if (avg < minMoney) {
                continue;
            }

            redMoney.residueTotal--;
            redMoney.residueMoney -= redPacket;
            return redPacket;
        }
    }

    public static boolean checkRule(RedMoney redMoney) {
        // 红包总额。单位，分
        int money = redMoney.money;
        // 红包数量
        int total = redMoney.total;

        if (money > MAX_MONEY || money < MIN_MONEY) {
            return false;
        }
        //人均分配金额,至少每人分得minMoney
        int avg = (int) (money / total);
        if (avg < MIN_MONEY) {
            return false;
        }
        return true;
    }

    public static void main(String[] args) {
        // 模拟正常的一次抢红包的过程
        RedMoney redMoney = new RedMoney(2, 2);
        for (int i = 0; i < 10; i++) {
            System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
            int money = RedPacket.get(redMoney);
            System.out.println(i + ". 获得红包：" + money + "\t\t" + redMoney);
        }

        // 模拟多个红包
    	/*StopWatch sw = new StopWatch();
    	sw.start("抢红包");
		for (int j = 0; j < 1000 * 1000 * 100; j++) {
			try {
				RedMoney redMoney = new RedMoney(100*100, 10);
				for (int i = 0; i < 10; i++) {
					RedPacket.get(redMoney);
				}

				if(redMoney.residueMoney != 0) {
					System.out.println("计算异常");
				}

				if(j % (1000 * 1000 ) == 0) {
					System.out.println("running");
				}

			} catch (StackOverflowError e) {
				System.out.println("栈溢出");
			}
		}
		sw.stop();
		System.out.println(sw.prettyPrint());*/

    }

    /**
     * 红包
     */
    public static class RedMoney {

        /**
         * 红包总额。单位，分
         */
        public int money;
        /**
         * 红包剩余。单位，分
         */
        public int residueMoney;

        /**
         * 红包数量
         */
        public int total;
        /**
         * 红包剩余数量
         */
        public int residueTotal;

        public RedMoney(int money, int total) {
            this.money = money;
            this.residueMoney = money;
            this.total = total;
            this.residueTotal = total;
        }

        @Override
        public String toString() {
            return "RedMoney [money=" + money + ", residueMoney=" + residueMoney
                    + ", total=" + total + ", residueTotal=" + residueTotal
                    + "]";
        }

    }

}
